package com.hongji.lms5xxcommunicator.utils;

import com.hongji.lms5xxcommunicator.grid.region.CellPeak;
import com.hongji.lms5xxcommunicator.grid.region.Point;
import org.eclipse.milo.opcua.stack.core.types.builtin.NodeId;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Locale;

/**
 * <p>通用工具集合。</p>
 *
 * <p><b>职责：</b></p>
 * <ul>
 *   <li>提供 OPC UA {@link NodeId} 的灵活解析。</li>
 *   <li>提供区域峰值/格峰值数据的格式化输出：{@link Point}、{@link CellPeak}。</li>
 * </ul>
 *
 * <p><b>用法：</b></p>
 * <ol>
 *   <li>调用 {@link #parseFlexibleNodeId(String)} 解析字符串为 {@link NodeId}。</li>
 *   <li>调用 {@link #formatPoint(Point)} 或 {@link #formatCell(CellPeak)} 生成简洁可读的日志文本。</li>
 * </ol>
 *
 * @since 1.0
 */
public final class Utils {

    private static final Logger log = LoggerFactory.getLogger(Utils.class);

    /** 工具类，禁止实例化。 */
    private Utils() { /* no-op */ }

    // =========================
    //       文本格式化
    // =========================

    /**
     * <p>将 {@link Point} 格式化为短文本。</p>
     *
     * @param mp 区域内“原始点最大值”快照；可为 {@code null}
     * @return 形如 {@code (x=..., y=..., z=..., id=...)} 的字符串；`null` 时返回字符串 "null"
     */
    public static String formatPoint(Point mp) {
        if (mp == null) return "null";
        return String.format("(x=%.3f, y=%.3f, z=%.3f, id=%s)",
                mp.getX(), mp.getY(), mp.getZ(), String.valueOf(mp.getId()));
    }

    /**
     * <p>将 {@link CellPeak} 格式化为短文本。</p>
     *
     * @param cp 区域内“格均值最大值”快照；可为 {@code null}
     * @return 形如 {@code (row=..., col=..., meanZ=...)} 的字符串；`null` 时返回字符串 "null"
     */
    public static String formatCell(CellPeak cp) {
        if (cp == null) return "null";
        return String.format("(row=%d, col=%d, meanZ=%.3f)",
                cp.getRow(), cp.getCol(), cp.getMeanZ());
    }

    // =========================
    //        NodeId 解析
    // =========================

    /**
     * <p>字符串 {@link NodeId} 解析（更宽松）。</p>
     *
     * <p>支持两类输入：</p>
     * <ul>
     *   <li><b>宽松键值风格</b>：例如 {@code ns=4;id=2}、{@code ns=2;i=123}（会解析成 numeric NodeId）。</li>
     *   <li><b>Milo 标准风格</b>：例如 {@code ns=4;s=Tag}、{@code ns=1;g=...} —— 直接走 {@link NodeId#parse(String)}。</li>
     * </ul>
     *
     * <p><b>说明：</b>当匹配到 {@code id=} 或 {@code i=} 时，尝试走“命名空间 + 整型标识”的构造；
     * 否则回退到 {@link NodeId#parse(String)}。任何解析错误会记录警告日志并抛出 {@link IllegalArgumentException}。</p>
     *
     * @param s 待解析的字符串，非空
     * @return 解析得到的 {@link NodeId}
     * @throws IllegalArgumentException 当输入为空或格式非法时
     */
    public static NodeId parseFlexibleNodeId(String s) {
        if (s == null) {
            log.warn("NodeId 解析失败：输入为 null");
            throw new IllegalArgumentException("nodeId string must not be null");
        }
        final String src = s.trim();
        if (src.isEmpty()) {
            log.warn("NodeId 解析失败：输入为空字符串");
            throw new IllegalArgumentException("nodeId string must not be empty");
        }

        final String lower = src.toLowerCase(Locale.ROOT);

        // 兼容 "ns=4;id=2" 或 "ns=2;i=123" 这类写法（整数 NodeId）
        if (lower.contains("ns=") && (lower.contains(";id=") || lower.contains(";i="))) {
            try {
                String[] parts = src.split(";");
                int ns = -1;
                Integer id = null;

                for (String part : parts) {
                    String p = part.trim();
                    int eq = p.indexOf('=');
                    if (eq <= 0) continue;
                    String k = p.substring(0, eq).trim().toLowerCase(Locale.ROOT);
                    String v = p.substring(eq + 1).trim();
                    if ("ns".equals(k)) {
                        ns = Integer.parseInt(v);
                    } else if ("id".equals(k) || "i".equals(k)) {
                        id = Integer.valueOf(v);
                    }
                }

                if (ns < 0 || id == null) {
                    log.warn("NodeId 解析失败：缺少 ns 或 id/i，输入={}", src);
                    throw new IllegalArgumentException("invalid nodeId string: " + src);
                }

                NodeId nodeId = new NodeId(ns, id);
                log.debug("NodeId 解析成功（numeric）：ns={}, id={}", ns, id);
                return nodeId;
            } catch (RuntimeException ex) {
                log.warn("NodeId 解析异常（numeric 分支）：input='{}', err={}", src, ex.getMessage());
                throw new IllegalArgumentException("invalid nodeId string: " + src, ex);
            }
        }

        // 其他情况交给 Milo 的标准解析器（支持 ns=...;s=... / g=... 等）
        try {
            NodeId nodeId = NodeId.parse(src);
            log.debug("NodeId 解析成功（standard）：{}", nodeId);
            return nodeId;
        } catch (RuntimeException ex) {
            log.warn("NodeId 解析异常（standard 分支）：input='{}', err={}", src, ex.getMessage());
            throw new IllegalArgumentException("invalid nodeId string: " + src, ex);
        }
    }
}
